home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 23 / Amiga Format AFCD23 (Feb 1998, Issue 107).iso / -in_the_mag- / emulation / consoles / vision-8 / sources / msx1.c < prev    next >
C/C++ Source or Header  |  1997-12-12  |  18KB  |  612 lines

  1. /** Vision8: CHIP8 emulator *************************************************/
  2. /**                                                                        **/
  3. /**                                 MSX1.c                                 **/
  4. /**                                                                        **/
  5. /** This file contains the MSX-1 and Coleco ADAM implementation            **/
  6. /**                                                                        **/
  7. /** Copyright (C) Marcel de Kogel 1997                                     **/
  8. /**     You are not allowed to distribute this software commercially       **/
  9. /**     Please, notify me, if you make any changes to this file            **/
  10. /****************************************************************************/
  11.  
  12. #include "CHIP8.h"
  13. #include "VDP.h"
  14.  
  15. #include <stdio.h>
  16. #include <string.h>
  17. #include <sys.h>
  18. #include <stdlib.h>
  19.  
  20. #ifdef MSX                                      /* Define the VDP I/O port  */
  21. #define _VDP_CTRL_PORT  99h                     /* addresses                */
  22. #define _VDP_DATA_PORT  98h
  23. #else
  24. #define _VDP_CTRL_PORT  0bfh
  25. #define _VDP_DATA_PORT  0beh
  26. #endif
  27.  
  28. #define CHRTAB          0x1800                  /* name table               */
  29. #define CHRGEN          0x0000                  /* pattern generator        */
  30. #define COLTAB          0x2000                  /* colour table             */
  31. #define SPRTAB          0x3800                  /* sprite pattern generator */
  32. #define SPRGEN          0x1d00                  /* sprite attribute table   */
  33.  
  34. #define _CHRTAB         1800h                   /* same for inline assembly */
  35. #define _CHRGEN         0000h                   /* routines                 */
  36. #define _COLTAB         2000h
  37. #define _SPRTAB         3800h
  38. #define _SPRGEN         1d00h
  39.  
  40. static byte picture[16384];                     /* background picture data  */
  41.                                                 /* old VRAM contents are    */
  42.                                                 /* here as well             */
  43. static byte sync=1;                             /* if 0, do not sync to VDP */
  44.                                                 /* clock                    */
  45. static byte uperiod=1;                          /* number of interrupts per */
  46.                                                 /* screen update            */
  47.  
  48. /****************************************************************************/
  49. /* Turn sound on                                                            */
  50. /****************************************************************************/
  51. void chip8_sound_on (void)
  52. {
  53. #ifdef MSX
  54.  outp (0xa0,7);
  55.  outp (0xa1,0x3e);
  56. #else
  57.  outp (0xff,0x90);
  58. #endif
  59. }
  60.  
  61. /****************************************************************************/
  62. /* Turn sound off                                                           */
  63. /****************************************************************************/
  64. void chip8_sound_off (void)
  65. {
  66. #ifdef MSX
  67.  outp (0xa0,7);
  68.  outp (0xa1,0x3f);
  69. #else
  70.  outp (0xff,0x9f);
  71. #endif
  72. }
  73.  
  74. /****************************************************************************/
  75. /* Initialise the sound chip                                                */
  76. /****************************************************************************/
  77. static void init_sound (void)
  78. {
  79.  chip8_sound_off ();
  80. #ifdef MSX
  81.  outp (0xa0,0);                                 /* set frequency divider to */
  82.  outp (0xa1,0);
  83.  outp (0xa0,1);
  84.  outp (0xa1,1);
  85.  outp (0xa0,8);                                 /* maximum volume           */
  86.  outp (0xa1,0x0f);
  87. #else
  88.  outp (0xff,0x80);                              /* set frequency divider    */
  89.  outp (0xff,0x12);
  90. #endif
  91. }
  92.  
  93. /****************************************************************************/
  94. /* Update the display                                                       */
  95. /****************************************************************************/
  96. static void update_display (void)
  97. {
  98. #asm
  99.         global _chip8_display
  100.         ld      c,_VDP_CTRL_PORT
  101.         ld      de,_SPRTAB+4000h                ; set write address to
  102.         out     (c),e                           ; sprite attribute table
  103.         out     (c),d
  104.         dec     c
  105.         ld      hl,_chip8_display
  106.         ld      d,8                             ; 8 columns, 8 pixels wide
  107. 1:      ld      e,16                            ; 16 pixels high
  108. 2:      ld      a,(hl)                          ; get first pixel
  109.         inc     hl                              ; increment pointer
  110.         and     80h                             ; mask out unused bits
  111.         ld      b,a                             ; save it
  112.         ld      a,(hl)                          ; get second pixel
  113.         inc     hl                              ; increment pointer
  114.         and     40h                             ; mask out unused bits
  115.         or      b                               ; OR with previous pixel
  116.         ld      b,a                             ; save it
  117.         ld      a,(hl)                          ; ...
  118.         inc     hl
  119.         and     20h
  120.         or      b
  121.         ld      b,a
  122.         ld      a,(hl)
  123.         inc     hl
  124.         and     10h
  125.         or      b
  126.         ld      b,a
  127.         ld      a,(hl)
  128.         inc     hl
  129.         and     08h
  130.         or      b
  131.         ld      b,a
  132.         ld      a,(hl)
  133.         inc     hl
  134.         and     04h
  135.         or      b
  136.         ld      b,a
  137.         ld      a,(hl)
  138.         inc     hl
  139.         and     02h
  140.         or      b
  141.         ld      b,a
  142.         ld      a,(hl)
  143.         and     01h
  144.         or      b                               ; ...
  145.         out     (c),a                           ; update VRAM
  146.         push    de
  147.         ld      de,57                           ; update pointer to next row
  148.         add     hl,de
  149.         pop     de
  150.         dec     e
  151.         jp      nz,2b                           ; update next row
  152.         push    de
  153.         ld      de,-16*64+8                     ; update next column
  154.         add     hl,de
  155.         pop     de
  156.         dec     d
  157.         jp      nz,1b
  158.  
  159.         ld      hl,_chip8_display               ; now update lower half of
  160.         ld      de,64*16                        ; display
  161.         add     hl,de
  162.         ld      d,8
  163. 1:      ld      e,16
  164. 2:      ld      a,(hl)
  165.         inc     hl
  166.         and     80h
  167.         ld      b,a
  168.         ld      a,(hl)
  169.         inc     hl
  170.         and     40h
  171.         or      b
  172.         ld      b,a
  173.         ld      a,(hl)
  174.         inc     hl
  175.         and     20h
  176.         or      b
  177.         ld      b,a
  178.         ld      a,(hl)
  179.         inc     hl
  180.         and     10h
  181.         or      b
  182.         ld      b,a
  183.         ld      a,(hl)
  184.         inc     hl
  185.         and     08h
  186.         or      b
  187.         ld      b,a
  188.         ld      a,(hl)
  189.         inc     hl
  190.         and     04h
  191.         or      b
  192.         ld      b,a
  193.         ld      a,(hl)
  194.         inc     hl
  195.         and     02h
  196.         or      b
  197.         ld      b,a
  198.         ld      a,(hl)
  199.         and     01h
  200.         or      b
  201.         out     (c),a
  202.         push    de
  203.         ld      de,57
  204.         add     hl,de
  205.         pop     de
  206.         dec     e
  207.         jp      nz,2b
  208.         push    de
  209.         ld      de,-16*64+8
  210.         add     hl,de
  211.         pop     de
  212.         dec     d
  213.         jp      nz,1b
  214. #endasm
  215. }
  216.  
  217. /****************************************************************************/
  218. /* The library version of rand() is terribly slow on a Z80 and we don't     */
  219. /* really need good precision                                               */
  220. /****************************************************************************/
  221. #asm
  222.         psect   bss
  223. old_rnd:defb    0
  224.         psect   text
  225.         global  _rand
  226. _rand:  ld      a,r                             ; get refresh register
  227.         ld      b,a                             ; save it
  228.         ld      a,(old_rnd)                     ; get previous random value
  229.         add     a,b                             ; add refresh register
  230.         ld      (old_rnd),a                     ; save it
  231.         ld      l,a                             ; return value in hl. upper
  232.         ld      h,a                             ; byte should be unused
  233.         ret
  234. #endasm
  235.  
  236. /****************************************************************************/
  237. /* Update CHIP8 keyboard status                                             */
  238. /****************************************************************************/
  239. static void check_keys (void)
  240. {
  241.  byte i;
  242. #ifdef MSX
  243.  static byte joy_data[16]=
  244.  { 0,3,9,0, 5,2,8,0, 7,4,10,0, 0,0,0,0 };
  245.  static byte cursor_data[16]=
  246.  { 0,5,3,2, 9,8,0,0, 7,0,4,0, 10,0,0,0 };
  247.  for (i=0;i<16;++i) chip8_keys[i]=0;
  248.  outp (0xaa,7);
  249.  if ((inp(0xa9)&4)==0)
  250.  {
  251.   chip8_running=0;
  252.   return;
  253.  }
  254.  outp (0xaa,6);
  255.  if ((inp(0xa9)&0x20)==0)
  256.   chip8_reset ();
  257.  outp (0xa0,15);
  258.  outp (0xa1,0x03);
  259.  outp (0xa0,14);
  260.  i=inp (0xa2);
  261.  outp (0xa0,15);
  262.  outp (0xa1,0x4c);
  263.  outp (0xa0,14);
  264.  i&=inp (0xa2);
  265.  if ((i&0x30)!=0x30) chip8_keys[5]=1;
  266.  i=joy_data[(~i)&0x0f];
  267.  if (i) chip8_keys[i-1]=1;
  268.  outp (0xaa,0);
  269.  i=inp(0xa9);
  270.  if ((i&0x03)!=0x03) chip8_keys[1]=1;
  271.  if ((i&0x04)==0) chip8_keys[2]=1;
  272.  if ((i&0x08)==0) chip8_keys[3]=1;
  273.  if ((i&0x10)==0) chip8_keys[12]=1;
  274.  outp(0xaa,1);
  275.  i=inp(0xa9);
  276.  if ((i&0x80)==0) chip8_keys[14]=1;
  277.  if ((i&0x20)==0) chip8_keys[13]=1;
  278.  if ((i&0x10)==0) chip8_keys[12]=1;
  279.  if ((i&0x08)==0) chip8_keys[3]=1;
  280.  if ((i&0x04)==0) chip8_keys[2]=1;
  281.  outp(0xaa,2);
  282.  i=inp(0xa9);
  283.  if ((i&0x40)==0) chip8_keys[7]=1;
  284.  if ((i&0x08)==0) chip8_keys[15]=1;
  285.  if ((i&0x04)==0) chip8_keys[11]=1;
  286.  outp(0xaa,3);
  287.  i=inp(0xa9);
  288.  if ((i&0x80)==0) chip8_keys[7]=1;
  289.  if ((i&0x40)==0) chip8_keys[4]=1;
  290.  if ((i&0x08)==0) chip8_keys[14]=1;
  291.  if ((i&0x04)==0) chip8_keys[6]=1;
  292.  if ((i&0x02)==0) chip8_keys[9]=1;
  293.  if ((i&0x01)==0) chip8_keys[11]=1;
  294.  outp(0xaa,4);
  295.  i=inp(0xa9);
  296.  if ((i&0x80)==0) chip8_keys[13]=1;
  297.  if ((i&0x40)==0) chip8_keys[4]=1;
  298.  if ((i&0x20)==0) chip8_keys[6]=1;
  299.  if ((i&0x10)==0) chip8_keys[5]=1;
  300.  if ((i&0x08)==0) chip8_keys[10]=1;
  301.  if ((i&0x04)==0) chip8_keys[0]=1;
  302.  if ((i&0x02)==0) chip8_keys[9]=1;
  303.  if ((i&0x01)==0) chip8_keys[8]=1;
  304.  outp(0xaa,5);
  305.  i=inp(0xa9);
  306.  if ((i&0x80)==0) chip8_keys[10]=1;
  307.  if ((i&0x20)==0) chip8_keys[0]=1;
  308.  if ((i&0x10)==0) chip8_keys[5]=1;
  309.  if ((i&0x08)==0) chip8_keys[15]=1;
  310.  if ((i&0x01)==0) chip8_keys[8]=1;
  311.  outp(0xaa,8);
  312.  i=inp(0xa9);
  313.  if ((i&0x01)==0) chip8_keys[5]=1;
  314.  i=cursor_data[((~i)>>4)&0x0f];
  315.  if (i) chip8_keys[i-1]=1;
  316. #else
  317.  static byte keypad_table[16]=
  318.  { 0,9,5,6,0,8,12,3,0,11,1,10,4,2,7,0 };
  319.  static byte joypad_table[16]=
  320.  { 0,3,7,4,9,0,10,0,5,2,0,0,8,0,0,0 };
  321.  static byte keyboard_table[256]=
  322.  {
  323.    0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,     /* 00 */
  324.    0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,     /* 10 */
  325.    0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0,13,12, 4,16, 0,     /* 20 */
  326.    3, 2, 3, 4,13, 0, 0, 0,  0, 2, 0,15, 0, 0, 0, 0,     /* 30 */
  327.    0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,     /* 40 */
  328.    0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0,14, 0, 0, 0, 0,     /* 50 */
  329.    0, 8, 0,12,10, 7,15, 0,  0, 5, 8, 9,10, 1,11, 6,     /* 60 */
  330.    7, 5,14, 9, 0, 0,16, 6,  1, 0,11, 0, 0, 0, 0, 0,     /* 70 */
  331.    0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,     /* 80 */
  332.    0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,     /* 90 */
  333.    0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,     /* a0 */
  334.    0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,     /* b0 */
  335.    0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,     /* c0 */
  336.    0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,     /* d0 */
  337.    0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,     /* e0 */
  338.    0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0      /* f0 */
  339.  };
  340.  static byte joypad,keypad_1,keypad_2;
  341.  for (i=0;i<16;++i) chip8_keys[i]=0;
  342.  if ((i=bdos(6,0xff))!=0)
  343.  {
  344. #asm
  345.   di
  346. #endasm
  347.   if (i==27)
  348.   {
  349.    chip8_running=0;
  350.    return;
  351.   }
  352.   if (i==24) chip8_reset();
  353.   i=keyboard_table[i];
  354.   if (i) chip8_keys[i-1]=1;
  355.  }
  356.  outp (0xc0,0);
  357.  joypad=~(inp(0xfc)&inp(0xff));
  358.  outp (0x80,0);
  359.  keypad_1=inp(0xfc)^0x40;
  360.  keypad_2=inp(0xff)^0x40;
  361.  outp (0xc0,0);
  362.  joypad|=(keypad_1|keypad_2)&0x40;
  363.  if ((joypad|keypad_1|keypad_2)&0x40)
  364.   chip8_keys[5]=1;
  365.  joypad&=15;
  366.  keypad_1&=15;
  367.  keypad_2&=15;
  368.  if (keypad_1==4 || keypad_1==8)
  369.   chip8_keys[5]=1;
  370.  else
  371.  {
  372.   i=keypad_table[keypad_1];
  373.   if (i) chip8_keys[i-1]=1;
  374.  }
  375.  if (keypad_2==4 || keypad_2==8)
  376.   chip8_keys[5]=1;
  377.  else
  378.  {
  379.   i=keypad_table[keypad_2];
  380.   if (i) chip8_keys[i-1]=1;
  381.  }
  382.  i=joypad_table[joypad];
  383.  if (i) chip8_keys[i-1]=1;
  384. #endif
  385. }
  386.  
  387. /****************************************************************************/
  388. /* Update keyboard and display, sync emulation with VDP clock               */
  389. /****************************************************************************/
  390. void chip8_interrupt (void)
  391. {
  392.  static int ucount=1;
  393.  if (!--ucount)
  394.  {
  395.   update_display ();
  396.   check_keys ();
  397.   ucount=uperiod;
  398.  }
  399.  if (sync) while ((vdp_read_status()&0x80)==0);
  400.  else vdp_read_status();
  401. }
  402.  
  403. /****************************************************************************/
  404. /* Initialise the VDP                                                       */
  405. /****************************************************************************/
  406. static void init_vdp (void)
  407. {
  408.  static byte sprite_table[128]=
  409.  {
  410.   63,64,0,15,
  411.   63,96,4,15,
  412.   63,128,8,15,
  413.   63,160,12,15,
  414.   95,64,16,15,
  415.   95,96,20,15,
  416.   95,128,24,15,
  417.   95,160,28,15,
  418.   208
  419.  };
  420.  word i,j;
  421.  /* turn display off and set registers */
  422.  vdp_set_register (1,0x83);
  423. #ifdef MSX
  424.  vdp_set_register (8,0x08);
  425. #endif
  426.  vdp_set_register (0,0x02);
  427.  vdp_set_register (2,0x06);
  428.  vdp_set_register (3,0xff);
  429.  vdp_set_register (4,0x03);
  430.  vdp_set_register (5,0x3a);
  431.  vdp_set_register (6,0x07);
  432.  vdp_set_register (7,0xf0);
  433.  /* write VRAM and get old contents */
  434.  memcpy (picture+SPRGEN,sprite_table,sizeof(sprite_table));
  435.  for (i=0;i<768;++i) picture[CHRTAB+i]=(byte)i;
  436.  for (i=0,j=0;i<128;++i,j+=128)
  437.  {
  438.   vdp_read_video_ram (j,sprite_table,128);
  439.   vdp_write_video_ram (j,picture+j,128);
  440.   memcpy (picture+j,sprite_table,128);
  441.  }
  442.  vdp_read_status ();
  443.  vdp_set_register (1,0xc3);
  444. }
  445.  
  446. /****************************************************************************/
  447. /* Reset the VDP to its original status                                     */
  448. /****************************************************************************/
  449. static void reset_vdp (void)
  450. {
  451.  static byte vdp_regs[]=
  452.  {
  453. #ifdef MSX
  454.   0x00,0x70,0x00,0x80,0x01,0x36,0x07,0xf4,
  455.   0x08,0x02,0x00,0x00,0x00,0x00,0x00,0x00
  456. #else
  457.   0x00,0xd0,0x0d,0x00,0x07,0x00,0x00,0x4f
  458. #endif
  459.  };
  460.  byte i;
  461.  vdp_set_register (1,0x83);
  462.  vdp_write_video_ram (0,picture,16384);
  463.  for (i=0;i<sizeof(vdp_regs);++i)
  464.   if (i!=9) vdp_set_register (i,vdp_regs[i]);
  465.  vdp_read_status ();
  466. }
  467.  
  468. /****************************************************************************/
  469. /* Compare ASCII-$ string with ASCII-zero one                               */
  470. /****************************************************************************/
  471. static int __strcmp (char *s1,char *s2)
  472. {
  473.  char s[20],i;
  474.  /* Convert ASCII-$ string to ASCII-zero one */
  475.  for (i=0;i<sizeof(s)-1 && s1[i]!='$';++i) s[i]=s1[i];
  476.  s[i]='\0';
  477.  /* Convert upper case characters to lower case ones */
  478.  for (i=0;s2[i];++i)
  479.   if (s2[i]>='A' && s2[i]<='Z') s2[i]+='a'-'A';
  480.  /* Compare the strings */
  481.  return strcmp (s,s2);
  482. }
  483.  
  484. /****************************************************************************/
  485. /* Parse command line options and start emulation                           */
  486. /****************************************************************************/
  487. int main (int argc,char *argv[])
  488. {
  489.  FILE *f;
  490.  unsigned i,j,k,misparm;
  491.  /* Hi-Tech C can't handle initialised arrays of pointers */
  492.  char *options="h$bg$up$ip$s$";
  493.  char *p;
  494.  char *program=NULL,*bg="v8.bg";
  495.  chip8_iperiod=16;
  496.  printf ("Vision-8: Portable CHIP8 emulator\n"
  497.          "Copyright (C) 1997  Marcel de Kogel\n");
  498.  for (i=1,k=0;i<argc;++i)
  499.  {
  500.   misparm=0;
  501.   if (*argv[i]!='-')
  502.    switch (k++)
  503.    {
  504.     case 0:  program=argv[i];
  505.              break;
  506.     default: printf("Excessive filename '%s'\n",argv[i]);
  507.              return 0;
  508.    }
  509.   else
  510.   {    
  511.    for (p=options,j=0;*p;p=strchr(p,'$')+1,j++)
  512.     if (!__strcmp(p,argv[i]+1)) break;
  513.    switch (j)
  514.    {
  515.     case 0:
  516.      printf ("Usage: v8 [options] <filename>\n"
  517.              "Available options are:\n"
  518.              " -h          - Print this help page\n"
  519.              " -bg <file>  - Select file to load as\n"
  520.              "               background image [v8.bg]\n"
  521.              " -up <value> - Select number of\n"
  522.              "               interrupts per screen\n"
  523.              "               update [1]\n"
  524.              " -ip <value> - Select number of opcodes\n"
  525.              "               per interrupt [16]\n"
  526.              " -s <value>  - Select synchronisation\n"
  527.              "               mode [1]\n"
  528.              "               0 - Do not sync\n"
  529.              "                   emulation\n"
  530.              "               1 - Sync emulation to\n"
  531.              "                   VDP clock\n");
  532.              return 0;
  533.     case 1:  i++;
  534.              if (i<argc) bg=argv[i];
  535.              else misparm=1;
  536.              break;
  537.     case 2:  i++;
  538.              if (i<argc) uperiod=atoi(argv[i]);
  539.              else misparm=1;
  540.              break;
  541.     case 3:  i++;
  542.              if (i<argc) chip8_iperiod=atoi(argv[i]);
  543.              else misparm=1;
  544.              break;
  545.     case 4:  i++;
  546.              if (i<argc) sync=atoi(argv[i]);
  547.              else misparm=1;
  548.              break;
  549.     default: printf ("Wrong option '%s'\n",argv[i]);
  550.              return 1;
  551.    }
  552.    if (misparm)
  553.    {
  554.     printf("%s: Missing parameter\n",argv[i-1]);
  555.     return 1;
  556.    }
  557.   }
  558.  }
  559.  if (!program)
  560.  {
  561.   puts ("No program name given\n");
  562.   return 1;
  563.  }
  564.  printf ("Opening CHIP8 program %s... ",program);
  565.  f=fopen (program,"rb");
  566.  if (!f)
  567.  {
  568.   puts ("FAILED");
  569.   return 1;
  570.  }
  571.  printf ("OK\n Reading... ");
  572.  i=fread (chip8_mem+0x200,1,4096-0x200,f);
  573.  fclose (f);
  574.  if (i==0)
  575.  {
  576.   puts ("File is empty");
  577.   return 1;
  578.  }
  579.  printf ("%d bytes loaded\nOpening background picture %s... ",i,bg);
  580.  f=fopen (bg,"rb");
  581.  i=0;
  582.  if (f)
  583.  {
  584.   printf ("OK\n Reading... ");
  585.   if (fread(picture+CHRGEN,1,6144,f)==6144)
  586.    if (fread(picture+COLTAB,1,6144,f)==6144)
  587.     i=1;
  588.   fclose (f);
  589.  }
  590.  if (!i)
  591.  {
  592.   puts ("FAILED");
  593.   memset (picture,0,16384);
  594.  }
  595.  else puts ("OK");
  596.  printf ("Press any key to start emulation...");
  597.  getch ();
  598.  printf ("\n");
  599. #asm
  600.  di
  601. #endasm
  602.  init_vdp ();
  603.  init_sound ();
  604.  chip8 ();
  605.  chip8_sound_off ();
  606.  reset_vdp ();
  607. #asm
  608.  ei
  609. #endasm
  610.  return 0;
  611. }
  612.